

#ifndef MEMUTILS_H
#define MEMUTILS_H

#include "nodes/memnodes.h"


/*
 * MaxAllocSize，MaxAllocHugeSize
 * 		对分配大小的准任意限制。
 *
 * 注意：
 * 		不能保证较小的分配会成功，但较大的请求将被迅速拒绝。
 *
 * palloc() 强制执行 MaxAllocSize，选择与 TOAST 下 varlena 对象的限制大小相对应。
 * 参见 postgres.h 中的 VARSIZE_4B() 和相关宏。许多数据类型假设任何可分配的大小
 * 都可以在 varlena 头中表示。这个限制还允许调用者使用
 * "int" 变量作为分配的索引或长度。小心避免这些隐患的调用者可以通过
 * MemoryContextAllocHuge() 访问更高的限制。这两项限制允许代码假定它可以
 * 计算两倍的分配大小而不会发生溢出。
 */
#define MaxAllocSize	((Size) 0x3fffffff) /* 1 gigabyte - 1 */

#define AllocSizeIsValid(size)	((Size) (size) <= MaxAllocSize)

#define MaxAllocHugeSize	(SIZE_MAX / 2)

#define AllocHugeSizeIsValid(size)	((Size) (size) <= MaxAllocHugeSize)


/*
 * 标准顶级内存上下文。
 *
 * 只有 TopMemoryContext 和 ErrorContext 由
 * MemoryContextInit() 本身初始化。
 */
extern PGDLLIMPORT MemoryContext TopMemoryContext;
extern PGDLLIMPORT MemoryContext ErrorContext;
extern PGDLLIMPORT MemoryContext PostmasterContext;
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
extern PGDLLIMPORT MemoryContext MessageContext;
extern PGDLLIMPORT MemoryContext TopTransactionContext;
extern PGDLLIMPORT MemoryContext CurTransactionContext;

/* 这是对活动门户的内存上下文的瞬态链接： */
extern PGDLLIMPORT MemoryContext PortalContext;

/* 向后兼容宏 */
#define MemoryContextResetAndDeleteChildren(ctx) MemoryContextReset(ctx)


/*
 * mcxt.c 中与内存上下文类型无关的函数
 */
extern void MemoryContextInit(void);
extern void MemoryContextReset(MemoryContext context);
extern void MemoryContextDelete(MemoryContext context);
extern void MemoryContextResetOnly(MemoryContext context);
extern void MemoryContextResetChildren(MemoryContext context);
extern void MemoryContextDeleteChildren(MemoryContext context);
extern void MemoryContextSetIdentifier(MemoryContext context, const char *id);
extern void MemoryContextSetParent(MemoryContext context,
								   MemoryContext new_parent);
extern Size GetMemoryChunkSpace(void *pointer);
extern MemoryContext MemoryContextGetParent(MemoryContext context);
extern bool MemoryContextIsEmpty(MemoryContext context);
extern Size MemoryContextMemAllocated(MemoryContext context, bool recurse);
extern void MemoryContextStats(MemoryContext context);
extern void MemoryContextStatsDetail(MemoryContext context, int max_children,
									 bool print_to_stderr);
extern void MemoryContextAllowInCriticalSection(MemoryContext context,
												bool allow);

#ifdef MEMORY_CONTEXT_CHECKING
extern void MemoryContextCheck(MemoryContext context);
#endif
extern bool MemoryContextContains(MemoryContext context, void *pointer);

/* 复制和赋值上下文 ID 的便捷宏...但注意双重评估 */
#define MemoryContextCopyAndSetIdentifier(cxt, id) \
	MemoryContextSetIdentifier(cxt, MemoryContextStrdup(cxt, id))

/*
 * GetMemoryChunkContext
 * 		给定当前分配的块，确定它所属的上下文。
 *
 * 由任何内存上下文管理器分配的所有块都必须在之前
 * 存储相应 MemoryContext，没有填充，并在
 * 前面的 sizeof(void*) 字节中。当前分配的块必须包含
 * 指向其拥有上下文的回指针。回指针由 pfree() 和
 * repalloc() 用于查找要调用的上下文。
 */
#ifndef FRONTEND
static inline MemoryContext
GetMemoryChunkContext(void *pointer)
{
	MemoryContext context;

	/*
	 * 尝试检测传递给我们的虚假指针，尽管我们所能做的很有限。
	 * 根据推测，一个不是 MAXALIGNED 的指针并不是指向已分配的块。
	 */
	Assert(pointer != NULL);
	Assert(pointer == (void *) MAXALIGN(pointer));

	/*
	 * 好吧，查看上下文可能是安全的。
	 */
	context = *(MemoryContext *) (((char *) pointer) - sizeof(void *));

	AssertArg(MemoryContextIsValid(context));

	return context;
}
#endif

/*
 * 此例程处理内存上下文创建的与上下文类型无关的部分。
 * 它旨在从特定于上下文类型的创建例程调用，而不在其他地方调用。
 */
extern void MemoryContextCreate(MemoryContext node,
								NodeTag tag,
								const MemoryContextMethods *methods,
								MemoryContext parent,
								const char *name);

extern void HandleLogMemoryContextInterrupt(void);
extern void ProcessLogMemoryContextInterrupt(void);

/*
 * 内存上下文类型特定的函数
 */

/* aset.c */
extern MemoryContext AllocSetContextCreateInternal(MemoryContext parent,
												   const char *name,
												   Size minContextSize,
												   Size initBlockSize,
												   Size maxBlockSize);

/*
 * 此包装宏用于检查用作上下文名称的非常量字符串；
 * 这不再被支持。（如果您想提供变量标识符，请使用 MemoryContextSetIdentifier。）
 */
#ifdef HAVE__BUILTIN_CONSTANT_P
#define AllocSetContextCreate(parent, name, ...) \
	(StaticAssertExpr(__builtin_constant_p(name), \
					  "memory context names must be constant strings"), \
	 AllocSetContextCreateInternal(parent, name, __VA_ARGS__))
#else
#define AllocSetContextCreate \
	AllocSetContextCreateInternal
#endif

/* slab.c */
extern MemoryContext SlabContextCreate(MemoryContext parent,
									   const char *name,
									   Size blockSize,
									   Size chunkSize);

/* generation.c */
extern MemoryContext GenerationContextCreate(MemoryContext parent,
											 const char *name,
											 Size minContextSize,
											 Size initBlockSize,
											 Size maxBlockSize);

/*
 * 推荐的默认分配参数，适用于可能包含大量数据的“普通”上下文。
 */
#define ALLOCSET_DEFAULT_MINSIZE   0
#define ALLOCSET_DEFAULT_INITSIZE  (8 * 1024)
#define ALLOCSET_DEFAULT_MAXSIZE   (8 * 1024 * 1024)
#define ALLOCSET_DEFAULT_SIZES \
	ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE

/*
 * 推荐的分配参数适用于永远不需要包含大量数据的“小”上下文（例如，用于包含查询计划的上下文）。
 */
#define ALLOCSET_SMALL_MINSIZE	 0
#define ALLOCSET_SMALL_INITSIZE  (1 * 1024)
#define ALLOCSET_SMALL_MAXSIZE	 (8 * 1024)
#define ALLOCSET_SMALL_SIZES \
	ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE

/*
 * 推荐的分配参数适用于应从小开始但有时可能会增大的上下文。
 */
#define ALLOCSET_START_SMALL_SIZES \
	ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE


/*
 * 在AllocSet上下文中，超过此阈值的请求肯定会被单独分配（从而具有恒定的分配开销）。
 * 很少有调用者会对此感兴趣，但tuplesort/tuplestore需要知道。
 */
#define ALLOCSET_SEPARATE_THRESHOLD  8192

#define SLAB_DEFAULT_BLOCK_SIZE		(8 * 1024)
#define SLAB_LARGE_BLOCK_SIZE		(8 * 1024 * 1024)

#endif							/* MEMUTILS_H */
